﻿@using BlazorDexie.JsModule
@using Microsoft.AspNetCore.SignalR.Client
@using jihadkhawaja.chat.client
@using jihadkhawaja.chat.client.CacheDB
@using jihadkhawaja.chat.client.Core
@using jihadkhawaja.chat.client.Services

@inherits ProtectedViewBase

@inject SessionStorage SessionStorage
@inject StorageService StorageService
@inject IChatUser ChatUserService
@inject IChatMessage ChatMessageService
@inject IJSRuntime JSRuntime

<MudContainer MaxWidth="MaxWidth.Large" Class="my-4 pt-4">
    <MudStack Row AlignItems="AlignItems.Center" Justify="Justify.FlexStart" Class="mb-2">
        <MudIconButton Icon="@Icons.Material.Filled.ArrowBack" Size="Size.Large" Link="/channels" />
        <MudText Typo="Typo.h6" GutterBottom>@Title</MudText>
    </MudStack>

    @if (!IsLoading)
    {
        <MudStack>
            <MudPaper id="messages-container" Elevation="0" Class="pa-4"
                Style="height: calc(100vh - 280px); overflow-x:hidden;overflow-y:auto;">
                @if (Messages.Count() == 0)
                {
                    <MudText>No Messages</MudText>
                }
                else
                {
                    <MudStack>
                        @foreach (var message in Messages)
                        {
                            @if (message.DateCreated.Value.ToLocalTime().Date == DateTime.Today)
                            {
                                <MessageView DisplayName="@message.DisplayName" Content="@message.Content" 
                                Date="@message.DateCreated.Value.ToLocalTime().ToString("h:mm tt")"
                                             IsCurrentUser="@IsCurrentUserMessage(message.SenderId)"
                                             SentOnDate="@message.DateSent" SeenOnDate="@message.DateSeen" />
                            }
                            else if (message.DateCreated.Value.ToLocalTime().Date >= DateTime.Today.AddDays(-6))
                            {
                                <MessageView DisplayName="@message.DisplayName" Content="@message.Content" 
                                Date="@message.DateCreated.Value.ToLocalTime().ToString("ddd, h:mm tt")"
                                             IsCurrentUser="@IsCurrentUserMessage(message.SenderId)"
                                             SentOnDate="@message.DateSent" SeenOnDate="@message.DateSeen" />
                            }
                            else
                            {
                                <MessageView DisplayName="@message.DisplayName" Content="@message.Content" 
                                Date="@message.DateCreated.Value.ToLocalTime().ToString("dd/MM/yyyy h:mm tt")"
                                             IsCurrentUser="@IsCurrentUserMessage(message.SenderId)"
                                             SentOnDate="@message.DateSent" SeenOnDate="@message.DateSeen" />
                            }
                        }
                    </MudStack>
                }
            </MudPaper>
            <MudStack Row>
                <MudTextField @ref="inputMudTextField" DisableUnderLine
                          Value="InputContent"
                          ValueChanged="@((string s) => OnInputContent(s))" Immediate="true"
                          InputMode="InputMode.text" InputType="InputType.Text"
                          AutoFocus="true"
                          Label="Your message here" Variant="Variant.Filled" @onkeydown="@EnterPressed"
                          Lines="1" MaxLength="MaxMessageLength"
                          Counter="MaxMessageLength" Disabled="InputDisabled" />
                <MudIconButton Icon="@Icons.Material.Filled.Send" Variant="Variant.Filled"
                                  DisableElevation Class="mt-1 mb-6"
                               OnClick="SendMessage" ButtonType="ButtonType.Submit" Disabled="InputDisabled" />
            </MudStack>
        </MudStack>
    }
    else
    {
        <div style="width:100%;height:100%;text-align:center;">
            <MudProgressCircular Indeterminate Size="Size.Medium" />
        </div>
    }
</MudContainer>

@code {
    [Inject] ISnackbar Snackbar { get; set; } = null!;

    [Parameter]
    public string? Title { get; set; }
    [Parameter]
    public string? ChannelId { get; set; }

    private List<Message> Messages { get; set; }
    private bool IsLoading { get; set; } = true;

    MudTextField<string> inputMudTextField { get; set; } = null!;
    private string? InputContent { get; set; }
    private int MessageLinesCount { get; set; }
    private int MaxMessageLength { get; set; } = 300;
    private bool InputDisabled { get; set; }

    private EgrooDB CacheDB { get; set; } = null!;

    protected override void OnInitialized()
    {
        HubEvents();

        //init Cached DB
        var moduleFactory = new EsModuleFactory(JSRuntime);
        CacheDB = new EgrooDB(moduleFactory);

        Messages = new();
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            //get pending messages
            await ChatMessageService.SendPendingMessages();

            //fetch cached messages
            var messages = (await CacheDB.Messages
            .Where(nameof(Message.ChannelId))
            .IsEqual(GetChannelId())
            .Limit(50).ToList()) ?? new();

            //add cached messages
            Messages.AddRange(messages);

            //reorder
            Messages = Messages.OrderBy(x => x.DateCreated)
            .ToList();

            //update any unseen message
            _= UpdateUnseenMessages();

            IsLoading = false;

            await InvokeAsync(StateHasChanged);

            ScrollToMessagesEnd();
        }

        await base.OnAfterRenderAsync(firstRender);
    }

    private Guid GetChannelId()
    {
        Guid id;
        if (Guid.TryParse(ChannelId, out id))
        {
            return id;
        }

        return Guid.Empty;
    }

    private async Task SendMessage()
    {
        if (string.IsNullOrEmpty(InputContent) || string.IsNullOrWhiteSpace(InputContent))
        {
            return;
        }

        InputDisabled = true;

        InputContent = InputContent.Trim();

        Message message = new()
        {
            ChannelId = GetChannelId(),
            DisplayName = SessionStorage?.User?.Username,
            Content = InputContent,
            SenderId = SessionStorage.User.Id,
        };

        if (await ChatMessageService.SendMessage(message))
        {
            InputContent = string.Empty;
            await inputMudTextField.Clear();
        }
        else
        {
            Snackbar.Add("Failed to send message, Please check your connection and try again", MudBlazor.Severity.Error);
        }

        InputDisabled = false;

        await InvokeAsync(StateHasChanged);
    }

    public async Task EnterPressed(KeyboardEventArgs e)
    {
        if (e.Code == "Enter" || e.Code == "NumpadEnter")
        {
            await SendMessage();
        }
    }

    private void ScrollToMessagesEnd()
    {
        JSRuntime.InvokeVoidAsync("scrollToEnd", "messages-container");
    }

    private bool IsCurrentUserMessage(Guid senderid)
    {
        if (senderid == SessionStorage.User.Id)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    private void OnInputContent(string text)
    {
        InputContent = text;
    }

    private async Task UpdateUnseenMessages()
    {
        for (int i = 0; i < Messages.Count(); i++)
        {
            if (Messages[i].DateSeen is null && Messages[i].SenderId != SessionStorage.User?.Id)
            {
                await ChatMessageService.UpdateMessage(Messages[i]);
            }
        }
    }

    private void HubEvents()
    {
        MobileChatSignalR.HubConnection?.On<Message>("ReceiveMessage", async message =>
        {
            if (message.ChannelId != GetChannelId())
            {
                return;
            }

            Message? cachedMessage = Messages.FirstOrDefault(x => x.Id == message.Id);
            if (cachedMessage is not null)
            {
                await ChatMessageService.UpdatePendingMessage(message.Id);
                return;
            }

            User? senderUser = await ChatUserService.GetUserPublicInfo(message.SenderId);
            message.DisplayName = senderUser?.Username;

            Messages.Add(message);

            if (message.DateSeen is null && message.SenderId != SessionStorage.User?.Id)
            {
                await ChatMessageService.UpdateMessage(message);
            }

            await CacheDB.Messages.Add(message);

            await ChatMessageService.UpdatePendingMessage(message.Id);

            await InvokeAsync(StateHasChanged);

            ScrollToMessagesEnd();
        });

        MobileChatSignalR.HubConnection?.On<Message>("UpdateMessage", async message =>
        {
            if (message.ChannelId != GetChannelId())
            {
                return;
            }

            for (int i = 0; i < Messages.Count(); i++)
            {
                if (message.ReferenceId == Messages[i].ReferenceId)
                {
                    Messages[i].Id = message.Id;
                    Messages[i].DateSeen = message.DateUpdated;
                    Messages[i].DateSeen = message.DateSeen;

                    await CacheDB.Messages.Put(Messages[i], Messages[i].ReferenceId);

                    await InvokeAsync(StateHasChanged);

                    return;
                }
            }
        });
    }
}
